package cn.edu.sjtu.stap.output;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;

import org.aspectj.org.eclipse.jdt.internal.compiler.ast.*;

import cn.edu.sjtu.stap.cfg.*;
import cn.edu.sjtu.stap.cg.*;
import cn.edu.sjtu.stap.hg.*;
import cn.edu.sjtu.stap.sdg.SystemDependenceGraph;

public class ResultOutput {
    private static ResultOutput instance = null;
    

    public static ResultOutput getInstance() {
        if (instance == null) {
            instance = new ResultOutput();
        }
        return instance;
    }

    private PrintStream output = null;

    private String fileName = null;

    private boolean opened = false;

    public void open(boolean usePrevious) {
        try {
            // if (s == null || s.equals("")) {
            // output = System.out;
            // } else {
            if (!usePrevious || fileName == null) {
                if (output != null) {
                    close();
                }
                fileName = findNewFile();
            }
            output = new PrintStream(new FileOutputStream(fileName, true));
            // }
            opened = true;
        } catch (Exception e) {
            System.err.println("Cannot open file!");
            e.printStackTrace();
            output = System.out;
        }
    }

    public boolean isOpen() {
        return opened;
    }
    
    public void setFilename(String f) {
        if (output != null) {
            close();
        }
        this.fileName = BASE_DIR + f;
    }

    private static String BASE_DIR = null;

    static {
        setBaseDirectory("./test_results/"+new SimpleDateFormat("yyyy-MM-dd").format(new Date())+"/");
    }

    public static void setBaseDirectory(String dir) {
        File f = new File(dir);
        if (!f.exists()) {
            f.mkdir();
        }
        BASE_DIR = dir;
    }

    private String findNewFile() {
        long i = 1;
        String s = BASE_DIR + i + ".txt";

        while (new File(s).exists()) {
            i++;
            s = BASE_DIR + i + ".txt";
        }

        return s;
    }

    public void writeLine(String s) {
        if (!isOpen()) {
            open(true);
        }
        output.print(s);
        output.println();
    }

    public void writeCgs(final Map<AbstractMethodDeclaration, CallGraph> cgs) {
        if (!isOpen()) {
            open(true);
        }
        // System.out.println("Start to write Call Graphs.");
        Iterator<AbstractMethodDeclaration> i = cgs.keySet().iterator();
        while (i.hasNext()) {
            AbstractMethodDeclaration method = i.next();
            CallGraph g = cgs.get(method);
            output.println("------------------------------");
            output.println("The method is:");
            output.println(HierarchyGraph.getFullName(method));
            output.println("The CG is:");
            output.println(g);
            output.println();
        }
        // System.out.println("Finish writing Call Graphs.");
    }
    
    public void writeSdg(
            final Map<AbstractMethodDeclaration, SystemDependenceGraph> sdgs) {
        if (!isOpen()) {
            open(true);
        };
        
        if (sdgs == null) {
            output.println("The SDG graph is: null");
            return;
        }
        Iterator<AbstractMethodDeclaration> i = sdgs.keySet().iterator();      
        int dc = 0, cc = 0, ac = 0;     
        while (i.hasNext()) {       
            ASTNode node = i.next();
            SystemDependenceGraph g = sdgs.get(node);
            output.println("------------------------------");
            output.println("The method is:");
            output.println(node);
            output.println("Size of node set: " + g.getSizeOfNodes());
            output.println("Size of edge set: " + g.getSizeOfEdges());
            output.println("The SDG graph is:DCount: "+g.getDCount()+" CCount: "+g.getCCount()+" ACount: "+g.getACount());
            dc += g.getDCount();
            cc += g.getCCount();
            ac += g.getACount();
            output.println(g);
            output.println();
        }
        output.println("**********Statistic Data**********");
        output.println("The inter-SDG graphs is:DCount: "+dc+" CCount: "+cc+" ACount: "+ac);
        output.println("The Main Method's Count is:"+sdgs.keySet().size());   
        sdgs.clear();
    }
    
    public void writeIntraSdg(
            final Map<AbstractMethodDeclaration, SystemDependenceGraph> sdgs) {
        if (!isOpen()) {
            open(true);
        };
        if (sdgs == null) {
            output.println("The SDG graph is: null");
            return;
        }
        Iterator<AbstractMethodDeclaration> i = sdgs.keySet().iterator();
        while (i.hasNext()) {
            ASTNode node = i.next();
            SystemDependenceGraph g = sdgs.get(node);
            output.println("------------------------------");
            output.println("The method is:");
            output.println(node);
            output.println("The Intra SDG graph is: DCount: "+g.getDCount()+" CCount: "+g.getCCount());
            output.println(g);
//            dC += g.getDCount();
//            cC +=  g.getCCount();
//            output.println("The Accumulated graph number is :  DCount: " + dC
//                    + " CCount: " + cC);
            output.println();
        }        
    }



    public void writeIntraModuleCfg(final Map<AbstractMethodDeclaration, ControlFlowGraph> cfgs) {
        if (!isOpen()) {
            open(true);
        }
        // System.out.println("Start to write Intra-procedural Control Flow
        // Graphs.");
        Iterator<AbstractMethodDeclaration> i = cfgs.keySet().iterator();
        while (i.hasNext()) {
            AbstractMethodDeclaration method = i.next();
            ControlFlowGraph g = cfgs.get(method);
            output.println("---------------------------------------");
            output.println("The method is:");
            output.println(HierarchyGraph.getFullName(method));
            output.println();
            output.println("The IntraCFG is:");
            output.println(g);
            output.println();
        }
        // System.out.println("Finish writing Intra-procedural Control Flow
        // Graphs.");
    }

    public void writeInterModuleCfg(final Map<AbstractMethodDeclaration, ControlFlowGraph> cfgs) {
        if (!isOpen()) {
            open(true);
        }
        // System.out.println("Start to write Inter-procedural Control Flow
        // Graphs.");
        Iterator<AbstractMethodDeclaration> i = cfgs.keySet().iterator();
        while (i.hasNext()) {
            AbstractMethodDeclaration method = i.next();
            ControlFlowGraph g = cfgs.get(method);
            output.println();
            output.println("InterCFG: ----------------------------------");
            output.println("The program entry is:");
            output.println(HierarchyGraph.getFullName(method));
            output.println();
            output.println("Size of node set: " + g.getSizeOfNodes());
            output.println("Size of edge set: " + g.getSizeOfEdges());
            output.println("The InterCFG is:");
            output.println(g);
            output.println();
        }
        // System.out.println("Finish writing Inter-procedural Control Flow
        // Graphs.");
    }

    public void close() {
        try {
            if (output != null && output != System.out) {
                output.close();
            }
        } catch (Exception e) {
            System.err.println(e);
            throw new RuntimeException(e);
        } finally {
            output = null;
            opened = false;
        }
    }
}
